#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "monitor.h"
#include "house.h"

void iniciaMonitor(Monitor *m)
{
	pthread_mutex_init(&m->mutex, NULL);
	pthread_cond_init(&m->fornoOcupado, NULL);
	fimUsoDoForno = 0;
	jaFoiEscalonado = FALSE;
}

//Thread para o Raj
void threadMediador(Personagem *mediador)
{
	//Enquanto todos os usuários não terminaram de usar o forno.
	while(fimUsoDoForno < NUM_MAX_REQUERENTES_FORNO)
	{
		pthread_mutex_lock(&mediador->m->mutex);
		if(!jaFoiEscalonado)
		{
			escalonarFilaUsoForno(mediador);
			jaFoiEscalonado = TRUE;
		}
		pthread_mutex_unlock(&mediador->m->mutex);
	}
	pthread_exit(NULL);
}

void escalonarFilaUsoForno(Personagem *mediador)
{
	int tamanho = tamanhoDaFila();
	int i, j;
	Fila temp;

	int AmyPrimeiro = -1;
	int BernardettePrimeiro = -1;
	int PennyPrimeiro = -1;

	//Ordena pela prioridade definida
	for(i = 0; i < tamanho - 1; i++)
	{
		for(j = 0; j < tamanho - 1; j++)
		{
			//Sheldon ou Amy
			if((fila[j].numero == 0 || fila[j].numero == 1) && AmyPrimeiro == -1) AmyPrimeiro = qualPersonagemEstaNaFrente(0,1);
			//Howard ou Bernadette
			if((fila[j].numero == 4 || fila[j].numero == 5) && BernardettePrimeiro == -1) BernardettePrimeiro = qualPersonagemEstaNaFrente(4,5);
			//Leonard ou Penny
			if((fila[j].numero == 2 || fila[j].numero == 3) && PennyPrimeiro == -1) PennyPrimeiro = qualPersonagemEstaNaFrente(2,3);

			if(fila[j].numero > fila[j + 1].numero)
			{
				temp = fila[j];
				fila[j] = fila[j + 1];
				fila[j + 1] = temp;
			}
		}
	}

	//Já ordenado por prioridade, para evitar inanição, ordena por aqueles que usaram menos o forno
	for(i = 0; i < tamanho - 1; i++)
	{
		for(j = 0; j < tamanho - 1; j++)
		{
			if(fila[j].quantidadeDeUso > fila[j + 1].quantidadeDeUso)
			{
				temp = fila[j];
				fila[j] = fila[j + 1];
				fila[j + 1] = temp;
			}
		}
	}
	verificaDeadLock();
	realocaNamorados(AmyPrimeiro, BernardettePrimeiro, PennyPrimeiro);
}

void verificaDeadLock()
{
	if(verificaSeExisteNaFila(0) || verificaSeExisteNaFila(1))
	{
		if(verificaSeExisteNaFila(2) || verificaSeExisteNaFila(3))
		{
			//Para remover Howard ou Bernadette, é preciso ter Sheldon ou Amy e Leonard ou Penny
			if(verificaSeExisteNaFila(4))
			{
				removeFila(4);
				printf("Raj detectou um deadlock, liberando %s\n", qualPersonagem(4));
			}
			if(verificaSeExisteNaFila(5))
			{
				removeFila(5);
				printf("Raj detectou um deadlock, liberando %s\n", qualPersonagem(5));
			}
		}
	}
}

//Thread para cada personagem
void threadPersonagem(Personagem *personagem)
{
	//Enquanto cada personagem não termina sua quota de utilização do forno passado como argumento no programa.
	while(personagem->quantidadeUsoForno < qtdeUsoForno)
	{
		querUsarForno(personagem);
	}
	pthread_mutex_lock(&personagem->m->mutex);
	fimUsoDoForno++;
	pthread_mutex_unlock(&personagem->m->mutex);
	pthread_exit(NULL);
}

void querUsarForno(Personagem *personagem)
{
	printf("%s quer usar o forno\n", qualPersonagem(personagem->num_cliente));
	esperaRandomica();
	pthread_mutex_lock(&personagem->m->mutex);
	adicionaFila(personagem->num_cliente, personagem->quantidadeUsoForno);
	jaFoiEscalonado = FALSE;
	pthread_mutex_unlock(&personagem->m->mutex);
	sleep(1);
	forno(personagem);
}

void forno(Personagem *personagem)
{
	int condicao = TRUE;
	while(condicao)
	{
		pthread_mutex_lock(&personagem->m->mutex);
		if(fila[0].numero != personagem->num_cliente) pthread_cond_wait(&personagem->m->fornoOcupado, &personagem->m->mutex);
		pthread_cond_signal(&personagem->m->fornoOcupado);
		condicao = FALSE;
		removePrimeiroDaFila();
		printf("%s Comeca a usar o forno\n", qualPersonagem(personagem->num_cliente));
		personagem->quantidadeUsoForno++;
		esperaRandomica();
		printf("%s vai comer\n", qualPersonagem(personagem->num_cliente));
		pthread_mutex_unlock(&personagem->m->mutex);
		sleep(3);
	}
}

void esperaRandomica()
{
	double aleatorio = drand48();
	int i;
	for(i = 0; i < 100000000*aleatorio; i++) ;
}
